package io.ebean.config.dbplatform;

/**
 * Add ROWNUM column etc around SQL query to limit results.
 */
public class RownumSqlLimiter implements SqlLimiter {

  private final String rnum;

  private final boolean useFirstRowsHint;

  /**
   * Create with default inner rownum column alias and used FIRST_ROWS hint.
   */
  public RownumSqlLimiter() {
    this("rn_", true);
  }

  /**
   * Specify the inner rownum column alias and whether to include the FIRST_ROWS
   * hint.
   */
  public RownumSqlLimiter(String rnum, boolean useFirstRowsHint) {
    this.rnum = rnum;
    this.useFirstRowsHint = useFirstRowsHint;
  }

  @Override
  public SqlLimitResponse limit(SqlLimitRequest request) {

    // select *
    // from ( select /*+ FIRST_ROWS(n) */ ROWNUM rnum, a.*
    // from ( your_query_goes_here,
    // with order by ) a
    // where ROWNUM <=
    // :MAX_ROW_TO_FETCH )
    // where rnum >= :MIN_ROW_TO_FETCH;

    String dbSql = request.getDbSql();

    StringBuilder sb = new StringBuilder(60 + dbSql.length());

    int firstRow = request.getFirstRow();

    int lastRow = request.getMaxRows();
    if (lastRow > 0) {
      lastRow += firstRow;
    }

    sb.append("select * from ( ");

    sb.append("select ");
    if (useFirstRowsHint && request.getMaxRows() > 0) {
      sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ ");
    }

    sb.append("rownum ").append(rnum).append(", a.* ");
    sb.append(" from (");

    sb.append(" select ");
    if (request.isDistinct()) {
      sb.append("distinct ");
    }
    sb.append(dbSql);

    sb.append(NEW_LINE).append("  ) a ");
    if (lastRow > 0) {
      sb.append(" where rownum <= ").append(lastRow);
    }
    sb.append(" ) ");
    if (firstRow > 0) {
      sb.append(" where ");
      sb.append(rnum).append(" > ").append(firstRow);
    }

    String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());

    return new SqlLimitResponse(sql, true);
  }

}
